package com.citydo.webrtcspringboot.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.citydo.webrtcspringboot.config.ConfiguratorForClientIp;
import com.citydo.webrtcspringboot.entity.*;
import com.citydo.webrtcspringboot.websocket.stream.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Objects;

/**
 * @Data 由于@Data重写了hashCode()和equals()方法，
 * 会导致Set<Connection> remove元素时，找不到正确的元素，
 * 应用@Setter @Getter @ToString替换
 * @ServerEndpoint 不是单例模式
 */
@ServerEndpoint(value = "/", configurator = ConfiguratorForClientIp.class)
@Component
@Slf4j
@Getter
@Setter
@ToString
public class Connection {
    private Session session = null;
    private Session mateSession = null; //与之配对的session
    private boolean isConnected = false; //是否已经分配过

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) throws Exception {
        //        if(!session.isOpen()) return;
        String from = StreamManager.Instance().getConnect().get(session);
        if (from == null) return;
        System.out.println("from:" + from + " session:" + session + " connectSize:" + StreamManager.Instance().getWebKey().size());
//        String ip = TConnecter.getIP(session);
        this.session = null;
//        StreamManager.Instance()().disMateConnection(StreamManager.Instance()().getConnect().get(session));

        ConnectState fromConnect = StreamManager.Instance().getConnectKey().get(from);
        String identity = fromConnect.getIdentity();
        if (Objects.equals(identity, Message.CONNECTER_TWITCH)) {
            //推流端关闭时需要关闭所有与之连接的客户端
            StreamManager.Instance().closeTwitch(from);
        } else {
            //web端关闭时只关闭自身
            StreamManager.Instance().disConnectionWeb(from);
        }
        WebsocketService.Instance.RemoveSession(session);
        System.out.println("onClose id:" + fromConnect.getConnectionId() + " connectSize:" + StreamManager.Instance().getWebKey().size());
//        System.out.println("用户: " + ip + " 会话：" + session.getId() + ", 关闭连接，当前在线人数为: " + count);
    }

    /**
     * 连接发生错误时调用的方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
//        String ip = TConnecter.getIP(session);
//        log.error("用户连接错误: {}", ip);
        error.printStackTrace();
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param stringMessage 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session, String stringMessage) {
        Message message = JSON.parseObject(stringMessage, Message.class);
        SData sData = JSON.parseObject(message.getData(), SData.class);
        String from = message.getFrom();
        String to = message.getTo();
//        if(message.getIdentity().equals(Message.CONNECTER_WEB))
//        System.out.println("stringMessage:"+stringMessage);

//        System.out.println("onMessage identity:"+message.getIdentity()+" from:"+message.getFrom()+" to:"+message.getTo());

        String identity = message.getIdentity();
//        if (!this.isConnected){
//            ConnectState connectState = new ConnectState(connectionID,this.session,identity,false);
//            if(Objects.equals(identity, Identity.Web)){
//                System.out.println("Web connectionID:"+connectionID);
//                StreamManager.Instance()().getWebKey().put(connectionID, connectState);
//                StreamManager.Instance()().getWeb().put(this.session,connectionID);
//            }else{
//                System.out.println("Twitch connectionID:"+connectionID);
//                StreamManager.Instance()().getTwitchKey().put(connectionID, connectState);
//                StreamManager.Instance()().getTwitch().put(this.session,connectionID);
//            }
//            StreamManager.Instance()().getConnectKey().put(connectionID,connectState);
//            if(this.session!=null&&connectionID!=null){
//                StreamManager.Instance()().mateConnection(connectionID);
//                System.out.println(identity+" ConnectState:"+StreamManager.Instance()().getConnectKey().get(connectionID).toString());
//                this.isConnected = true;
//            }
//        }
        switch (message.getType()) {
            case Message.TYPE_COMMAND_CONNECT:
                onConnect(message);
                break;
            case Message.TYPE_COMMAND_DISCONNECT:
                onDisconnect(from, to);
                break;
            case Message.TYPE_COMMAND_OFFER:
                onOffer(from, to, message.getData(), identity);
                break;
            case Message.TYPE_COMMAND_ANSWER:
                onAnswer(from, to, message.getData());
                break;
            case Message.TYPE_COMMAND_CANDIDATE:
                onCandidate(from, to, message.getData());
                break;
            case Message.TYPE_COMMAND_STARTSTREAMING:
                onStartStream(sData.getConnectionId(), message.getData());
                break;
            case Message.TYPE_COMMAND_STATE:
                onState(message);
                break;
            case Message.TYPE_COMMAND_SENDMSG:
                onSendMsg(message);
                break;
            case Message.TYPE_COMMAND_UDP:
                WebsocketService.Instance.AddSession(session);
                break;
            default:
                break;
        }
    }

    //转发消息
    private void onSendMsg(Message message) {
        JSONObject dt = new JSONObject();
        dt.put("type", message.getType());
        dt.put("from", message.getFrom());
        dt.put("to", message.getTo());
        dt.put("polite", true);
        dt.put("data", message.getData());
        StreamManager.sendJson(StreamManager.Instance().getConnectKey().get(message.getTo()).getSession(), dt.toJSONString());
    }

//    private int count = 0;

    private void onState(Message message) {
//        if (count < 10 && message.getIdentity().equals(Message.CONNECTER_WEB)) {
//            System.out.println("type:" + message.getType() + "  to:" + message.getTo() + "  identity:" + message.getIdentity());
//            count++;
//        }
        String from = message.getFrom();
        ConnectStateTwitch twitch = StreamManager.Instance().getTwitchKey().get(from);
        if (twitch == null) {
            return;
        }
        twitch.setTime(ConnectStateTwitch.transTime());
    }

    private void onConnect(Message message) {
        try {
//            Message message = JSON.parseObject(messages,Message.class);
            String from = message.getFrom();
            String data = message.getData();
            String identity = message.getIdentity();
            String userName = message.getUserName();
            String token = message.getToken();
            log.info("onConnect:{from:" + from + " data:" + data + " identity:" + identity + "}");

//            ConnectState connectState = new ConnectState(from, this.session, identity, false);
            String to = "";
            //判断是否是独享用户
            UserGrade grade = UserGrade.SharedGroup;
            if (identity.equals(Message.CONNECTER_WEB)) {
                if (Objects.equals(userName, "admin"))
                    grade = UserGrade.UnSharedGroup;
            }
            StreamManager.Instance().Put(identity, from, this.session, grade);
/*        StreamManager.Instance().getConnectKey().put(from,connectState);
        StreamManager.Instance().getConnect().put(connectState.getSession(),from);
        if(Objects.equals(identity,Message.CONNECTER_TWITCH)) {
            StreamManager.Instance().getTwitch().put(connectState.getSession(), from);
            StreamManager.Instance().getTwitchKey().put(from, new ConnectStateTwitch(connectState));
        } else {//仅在客户端连接时进行配对
            StreamManager.Instance().getWeb().put(connectState.getSession(),from);
            StreamManager.Instance().getWebKey().put(from,new ConnectStateWeb(connectState));
            this.mateSession =

                treamManager.Instance().mateConnection(from);
            to = StreamManager.Instance().getWebKey().get(from).getMateConnectionId();
        }*/
            if (identity.equals(Message.CONNECTER_WEB)) {
                ActionManager.Instance().addEvent(ActionManager.ActionName.userEntryNotice,
                        (params) -> StreamManager.Instance().mateConnection((String)params[0], ""));
                ActionManager.Instance().invoke(ActionManager.ActionName.userEntryNotice, from);
/*                ConnectStateTwitch twitch = StreamManager.Instance().mateConnection(from, userName);
                long currentTime = ConnectStateTwitch.transTime();
                if (twitch!=null&&currentTime - twitch.getTime() >= ConnectStateTwitch.timeError && twitch.getTime() > 0) {
                    //服务器连接超时或已经崩溃
                    StreamManager.Instance().closeTwitch(twitch.getConnectionId());
                    try {
                        twitch.getSession().close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    System.out.println("服务器连接超时或已经崩溃,server is crashed or disconnect...");
                    twitch = StreamManager.Instance().mateConnection(from, userName);
                }*/
                to = StreamManager.Instance().getWebKey().get(from).getMateConnectionId();
            }
            JSONObject obj = new JSONObject();
            obj.put("type", "connect");
            obj.put("from", from);
            obj.put("to", to);
            obj.put("polite", true);
            StreamManager.sendJson(this.session, obj.toJSONString());
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    private void onDisconnect(String from, String to) {
        log.info("from:" + from + " to:" + to);
        Session fromConnectSession = StreamManager.Instance().getConnectKey().get(from).getSession();
        ConnectState fromConnectState = StreamManager.Instance().getConnectKey().get(from);
        if (Objects.equals(fromConnectState.getIdentity(), Message.CONNECTER_TWITCH)) {
            StreamManager.Instance().closeTwitch(from);
        } else {
            JSONObject fromJson = new JSONObject();
            fromJson.put("connectionId", to);
            fromJson.put("type", "disconnect");
            StreamManager.sendJson(fromConnectSession, fromJson.toJSONString());
        }
    }

    private void onOffer(String from, String to, String data, String identity) {
        if (to==null||to.isEmpty()) return;
        SData sd = JSON.parseObject(data, SData.class);
        ConnectState connectState = StreamManager.Instance().getConnectKey().get(from);
        ConnectState mateConnectState = StreamManager.Instance().getConnectKey().get(to);
//        System.out.println("from:"+from+" to:"+to);
        Session mateSession = StreamManager.Instance().getConnectKey().get(to).getSession();

        if (mateSession != null) {
            JSONObject obj = new JSONObject();
            Offer newOffer = new Offer(sd.getSdp(), System.currentTimeMillis(), false);
            obj.put("data", newOffer);
            obj.put("from", from);
            obj.put("to", to);
            obj.put("type", "offer");
            String output = obj.toJSONString();
            try {
                StreamManager.sendJson(mateSession, output);
            } catch (java.lang.IllegalStateException exception) {
                StreamManager.Instance().CloseStreamByIdentity(from, connectState.getIdentity());
            }
        }
    }

    private void onAnswer(String from, String to, String data) {
        if (to == null) return;
        SData sd = JSON.parseObject(data, SData.class);
        Session mateSession = StreamManager.Instance().getConnectKey().get(to).getSession();

        if (mateSession != null) {
            JSONObject obj = new JSONObject();
            Answer newAnswer = new Answer(sd.getSdp(), System.currentTimeMillis());
            obj.put("data", newAnswer);
            obj.put("from", from);
            obj.put("to", to);
            obj.put("type", "answer");
            String output = obj.toJSONString();
            try {
                StreamManager.sendJson(mateSession, output);
            } catch (java.lang.IllegalStateException exception) {
                StreamManager.Instance().CloseStreamByIdentity(from, StreamManager.Instance().getConnectKey().get(from).getIdentity());
            }
//            System.out.println(this.session.getId() + "-> " + output);
        }
    }

    private void onCandidate(String from, String to, String data) {
        SData sd = JSON.parseObject(data, SData.class);
        if (to == null) return;
        Session mateSession = StreamManager.Instance().getConnectKey().get(to).getSession();

        if (mateSession != null) {
            JSONObject obj = new JSONObject();
            Candidate candidate = new Candidate(sd.getCandidate(), sd.getSdpMLineIndex(), sd.getSdpMid(), System.currentTimeMillis());
            obj.put("data", candidate);
            obj.put("from", from);
            obj.put("to", to);
            obj.put("type", "candidate");
            String output = obj.toJSONString();
            try {
                StreamManager.sendJson(mateSession, output);
            } catch (java.lang.IllegalStateException exception) {
                StreamManager.Instance().CloseStreamByIdentity(from, StreamManager.Instance().getConnectKey().get(from).getIdentity());
            }
        }
//            System.out.println(this.session.getId() + "-> " + output);
    }

    public void onStartStream(String to, String data) {
    }
}
